#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include "buffer.h"


BUFFER*     
buf_new(int step, const char* filename, int mode)
{
    BUFFER  *buf;
    FILE    *fp;
    struct  stat fstat;

    if (step <= 0 )
        step = 4096;

    if (strlen(filename) >= 256){
        INFO("filename too long.\n");
        return NULL;
    }
    
    if (0 == mode)
    {
        if (0 != stat(filename, &fstat)) {
            INFO("stat file error!\n");
            return NULL;
        }
        fp = fopen(filename, "rw+");
    }else if (1 == mode){
        fp = fopen(filename, "w+");
    }else if (2 == mode) {
        fp = fopen(filename, "a+");
    }
    if (NULL == fp){
        INFO("fopen error! %s\n", filename);
        fprintf(stderr, "buffer_new error: %s, filename: %s\n", strerror(errno), filename);
        return NULL;
    }
    
    buf = (BUFFER*)emalloc(sizeof(BUFFER));
    if (NULL == buf){
        INFO("malloc buffer error!\n");
        return NULL;
    }

    memset(buf, 0, sizeof(BUFFER));
    buf->max = step;
    buf->mode = mode; 
    strcpy(buf->filename, filename);
    buf->file = fp; 
    buf->filesize = fstat.st_size; 

    buf->data = (char*)emalloc(step+1);
    if (NULL == buf->data)
    {
        efree(buf);
        INFO("malloc buffer->data error!\n");
        return NULL;
    }
    buf->data[step] = 0;
    
    return buf;
}


void        
buf_delete(BUFFER*  buf)
{
    if (NULL == buf)
        return ;

    if (buf->mode > 0) {
        buf_write_file(buf);
    }

    if (NULL != buf->file)
    {
        fclose(buf->file);
    }
    if (NULL != buf->data)
        efree(buf->data);

    efree(buf);
}

void        
buf_clear(BUFFER*  buf)
{
    if (NULL == buf)
        return ;
    
    if (buf->mode > 0) {
        buf_write_file(buf);
    }
    buf->data[0] = 0;
    buf->len = 0;
    buf->pos = 0;
    buf->eob= 0;
}

void        
buf_rset(BUFFER*  buf)
{
    if (NULL == buf)
        return ;
    buf_clear(buf);
    fseek(buf->file, 0L, SEEK_SET);
}

int 
buf_writen(BUFFER *buf, char *src, int srclen)
{
    int wsize = 0, idlesize, lsize;

    if (NULL == buf)
        return -1;
    
    if (buf->mode == 0)
        return 0;
    lsize = 0;
  
    //INFO("writen: %d\n", srclen);
    while (lsize < srclen) {
        idlesize = buf->max - buf->pos;
        if (idlesize == 0) {
            buf_write_file(buf);
            idlesize = buf->max - buf->pos;
        }
        
        wsize = (idlesize >= srclen-lsize) ? srclen-lsize: idlesize;
        memcpy(&buf->data[buf->pos], src+lsize, wsize);
        buf->pos += wsize;
        lsize += wsize;
    }
    //INFO("buffer pos: %d\n------------------\n", buf->pos); 
    return srclen;
}



int        
buf_readn(BUFFER*  buf, int size, char** save)
{
    int     msize, ret;

    if (NULL == buf)
        return -1;
    if (NULL == save)
        return -2;

    if (size < 0)
        return -3;
    
    if (buf->mode > 0)
        return 0;

    msize = buf->len - buf->pos;
    INFO("size: %d, msize: %d\n", size, msize);
    if (msize < size ) {
        ret = buf_read_file(buf);
        INFO("buf_read_file: %d, pos: %d, len:%d\n", ret, buf->pos, buf->len);
        if (0 == ret) {
            if (msize == 0)
                return 0;
            size = msize;
        }
    }    
    
    msize = buf->len - buf->pos;
    *save = &buf->data[buf->pos];
    size = (msize>=size)? size: msize;
    
    /*
    if (buf->len >= size) {
        buf->pos += size;
    }else{
        buf->pos += buf->len;
        size = buf->len;
    }*/

    buf->pos += size;
    INFO("buf_readn last: pos: %d, len: %d, msize: %d, size: %d\n", buf->pos, buf->len, msize, size);
    return size;

}

int
buf_read_file(BUFFER *buf)
{
    int     msize;
    int     readlen;
	//int		leave;
	
    if (NULL == buf)
        return -1;    
    if (buf->mode > 0)
        return 0;

    msize = buf->len - buf->pos; 
     
    readlen = buf->max;
    if (msize > 0){
        memmove(buf->data, &buf->data[buf->pos], msize);
        readlen -= msize;
        buf->len = msize;
        //buf->data[msize] = 0;
    }
    
    //leave = buf->filesize - ftell(buf->file);
    buf->len = fread(&buf->data[msize], 1, readlen, buf->file);    
    //INFO("buf_read_file return: %d\n", buf->len);
    if (buf->len <= readlen) {
        buf->len += msize;        
    }
    buf->pos = 0;
    buf->data[buf->len] = 0;
    
    //INFO("read: %s\n", &buf->data[msize]);
    return buf->len;
}

int 
buf_write_file(BUFFER *buf) 
{
    int ret;

    if (NULL == buf)
        return -1;
    if (buf->mode == 0)
        return 0;
    
    //INFO("buf_write_file...%d\n", buf->pos);
    ret = fwrite(buf->data, 1, buf->pos, buf->file);
    if (ret > 0) {
        buf->pos = 0;
        buf->len = 0; 
    } 
    return ret;
}

/*!
 返回当前buf->pos相当于在文件中的偏移位置
 */
int
buf_file_pos(BUFFER *buf)
{
    int pos;

    if (NULL == buf)
        return -1;

    pos = ftell(buf->file) - (buf->len-buf->pos);

    return pos;
}

/*!
 从buffer中读取出制定的数据
 \param buf
 \param flag  标记，0表示读取一行，1表示读取一个mail头部信息行
 \param save
 \param savelen
 */
int         
buf_getdata(BUFFER*  buf, int flag, char** save, int* savelen)
{
    char    *start, *begin;
    int     msize, ret;

    if (NULL == buf)
        return -1;
    if (NULL == save)
        return -2;
    if (NULL == savelen)
        return -3;
    if (buf->mode > 0)
        return 0;
    
    //INFO("start pos: %d, len: %d\n", buf->pos, buf->len);
    msize = buf->len - buf->pos; 
    //INFO("getdata: pos: %d, len: %d, msize: %d\n", buf->pos, buf->len, msize);
    // buffer中还剩下的少于512字节就重新加载
    if (msize == 0 || (msize <= 512  && buf->len > 512)) {
        ret = buf_read_file(buf);
        /*
        if (0 == ret) {            
            //return 0;
        }*/
    }    

    msize = buf->len - buf->pos; 
    //INFO("after read pos: %d, len: %d, msize: %d\n", buf->pos, buf->len, msize);
    begin = &buf->data[buf->pos];
    //INFO("begin: %d %d %d %d\n", begin[0], begin[1], begin[2], begin[3]);
    start = strchr(begin, '\n');
    //INFO("1 begin: %p, start: %p\n", begin, start);
    // 可不可能因为一行比较长，超出了buffer的范围，结果造成\n在更靠后的位置
    if (NULL == start) {
        INFO("getdata: pos: %d, len: %d\n", buf->pos, buf->len);
        *save = NULL;
        *savelen = 0;
        return -100;
    }   
   
    if (start >= begin + msize) {
        INFO("Error!, start is more than end.. start: %p, end: %p\n", start, begin + buf->len);
        *save = begin;
        *savelen = msize;
        buf->pos += msize;
        return msize;
    }
    //INFO("111111 pos: %d, len: %d\n", buf->pos, buf->len);
    if (flag == 1 && start-begin > 1) {
        while (start[1] == '\t' || start[1] == ' ') {
            // 会不会已经读到一行，但下一行却因为缓冲区满了还有部分在文件中呢？
            // 不太可能, 因为一行应该是不会超过512字节的
            start = strchr(start+1, '\n');
            if (NULL == start) { //还真的没找到？？
                INFO("not found \\n after \\t or <space>\n");
                *save = NULL;
                *savelen = 0;                
                return 0;
            }
        }
    }

    //INFO("222222 pos: %d, len: %d\n", buf->pos, buf->len);
    //INFO("2 begin: %p, start: %p\n", begin, start);
    *save = begin;
    *savelen = start - begin + 1; 
    buf->pos += *savelen;
    //INFO("end pos: %d, len: %d\n", buf->pos, buf->len);
    //INFO("return %d\n", *savelen);
    return *savelen;
}

int         
buf_getline(BUFFER*  buf, char** save, int* savelen)
{
    return buf_getdata(buf, 0, save, savelen);
}



int         
buf_getfield(BUFFER*  buf, char** save, int* savelen)
{
    return buf_getdata(buf, 1, save, savelen);
}

int
buf_ungetdata(BUFFER* buf, char** save, int* savelen, int flag)
{
    char *data;
    int  lineflag = 0; //换行标记数
    int  i;
    
    if (buf->mode > 0)
        return 0;

    data = buf->data;
    while (1) {
        lineflag = 0;
        //INFO("pos data: %c%c%c%c%c\n", data[buf->pos], data[buf->pos+1], data[buf->pos+2], data[buf->pos+3], data[buf->pos+4]);
        for (i = buf->pos-1; i >=0; i--) {
            //printf("%c", data[i]);
            if (data[i] == '\n') {
                if (0 == flag) {
                    lineflag++;
                    if (lineflag == 2) {
                        break;
                    }
                }else{
                    if (data[i+1] > 32) { // 换行符的下个字符是\t 空格都不算一行
                        lineflag++;
                        if (lineflag == 2) {
                            break;
                        }
                    }
                }
            }
        }
        //printf("\n i = %d, lineflag =%d\n", i, lineflag); 
        if (lineflag == 2) {
            *save = &buf->data[i+1];
            *savelen = buf->pos - i - 1;
            buf->pos = i + 1;
            break;
        }
        // 需要从文件中读取了
        if (i < 0) {
            int  readsize, readpos, readlen;
            int  fpos = ftell(buf->file);
         
            if (fpos == buf->len) {
                if (1 == lineflag) {
                    *save = &buf->data[0];
                    *savelen = buf->pos;
                    buf->pos = 0;
                    break;
                }
            }
            
            //INFO("read file ...\n");
            readsize = fpos>512 ? 512:fpos;
            //INFO("readsize: %d pos:%d max:%d len: %d\n", readsize, buf->pos, buf->max, buf->len);
            if (buf->pos + readsize > buf->max) {
                *savelen = 0;
                return -2;
            }
            
            readpos = fpos - readsize - buf->len;
            //INFO("readpos: %d, fpos: %d\n", readpos, fpos);
            fseek(buf->file, readpos, SEEK_SET);
            
            readlen = buf->len + readsize;
            if (readlen > buf->max) {
                readlen = buf->max;
            }
            //INFO("ftell:%d\n", ftell(buf->file));
            buf->len = fread(buf->data, 1, readlen, buf->file);    
            //INFO("read %d %s\n-----------------\n", buf->len, buf->data);
            buf->pos += readsize;
            buf->data[buf->len] = 0;
            if (0 == buf->len) {
                *savelen = 0;
                return -1;
            }
            //INFO("pos data: %s\n", &buf->data[buf->pos]);
        }else{
            INFO("not in this pos\n");
            break;
        }
    }

    return 0;
}

int
buf_ungetline(BUFFER* buf, char** save, int* savelen)
{
    return buf_ungetdata(buf, save, savelen, 0);
}

int
buf_ungetfield(BUFFER* buf, char** save, int* savelen)
{
    return buf_ungetdata(buf, save, savelen, 1);
}

/*!
 这个搜索仅仅是在当前缓冲区中搜索，不会再次读取数据的。
 注意这个会修改缓冲区位置指针
 */
char*
buf_find (BUFFER* buf, char *search)
{
    char *ret;
    char *start = &buf->data[buf->pos];
    if (buf->mode > 0)
        return NULL;
    ret = strstr(start, search);
    if (ret != NULL) {
        buf->pos += (ret - start);
    }
    return ret;
}

void        
buf_skip(BUFFER* buf, int ch)
{
    int ret;
    if (buf->mode > 0)
        return;
    while ((int)buf->data[buf->pos] == ch) {
        buf->pos++;
        if (buf->pos == buf->len) {
            ret = buf_read_file(buf);
            if (0 == ret) { // 读取不到了，应该是文件结束了。
                return;
            }
            continue;
        }
    }
}

void 
buf_skips(BUFFER *buf, char *bstr, int len)
{
    int i;
    int ch;
    int inx = buf->pos;
    
    if (buf->mode > 0)
        return;

    while (inx < buf->len) {
        ch = buf->data[inx];
        for (i = 0; i < len; i++) {
            if (ch == (int)bstr[i]) {
                inx++;
                break;
            }
        }
        if (i < len) {
            continue;
        }
        break;
    }
}



#ifdef MAIN_TEST_BUF

void echo(char* data, int dlen)
{
    int i;
    for (i = 0; i< dlen; i++)
    {
        printf("%c", data[i]);
    }

    printf("\n");
    
    for (i = 0; i< dlen; i++)
    {
        printf("%d ", data[i]);
    }
    
    printf("\n");
}


void buf_print(BUFFER  *buf)
{
    printf("pos=%d, len=%d, max=%d\n", buf->pos, buf->len, buf->max);
    printf("pos=%c%c%c\n", buf->data[buf->pos], buf->data[buf->pos+1], buf->data[buf->pos+2]);
}
GHashTable  *gmemhash;
int
main(int argc, char *argv[])
{
    char    **retstr;
    char    *data;
    BUFFER   *buf;
    int     ret, retlen;
    char    filename[256];
    int     count = 0;

    if (argc == 2)
    {
        strcpy(filename, argv[1]);
    }else{
        strcpy(filename, "/tmp/test.1.txt");
    }

    
    gmemhash = e_init(NULL);
    buf = buf_new(1024, filename, 1);
    if (NULL == buf)
    {
        printf("error!\n");
        exit(0);

    }
    

    while (1) {
        if (count >= 10)
            break;

        buf_writen(buf, "1234567890", 10);
        count++;
    }
    /*
    while ( ! feof(buf->file))
    {
        printf("----- buf_readn ------\n");
        ret = buf_readn(buf, 10, &data);
        buf_print(buf);

        printf("ret=%d\n", ret);

        echo(data, ret);
    }
    */

    /*
    while( (ret = buf_getfield(buf, &data, &retlen)) > 0)
    {
        //ret = buf_getline(buf, &data, &retlen); 
        //ret = buf_getfield(buf, &data, &retlen); 
        buf_print(buf);
        printf("ret=%d\n", retlen);
        echo(data, retlen);
        printf("======================================================\n");
    }
    */
   
    /*
    while (1) {
        printf("get:\n");
        ret = buf_getline(buf, &data, &retlen);
        if (ret <= 0)
            break;
        {
            int i;
            for (i = 0; i < retlen; i++) {
                printf("%c", data[i]);
            }
            printf("\n");
        }
        printf("len:%d pos:%d unget:\n", retlen, buf->pos);
        buf_ungetline(buf, &data, &retlen);
        {
            int i;
            for (i = 0; i < retlen; i++) {
                printf("%c", data[i]);
            }
            printf("\n");
        }

        printf("len:%d, pos:%d\n", retlen, buf->pos);
        //buf_getline(buf, &data, &retlen);
        ret = buf_getline(buf, &data, &retlen);
        printf("ret = %d\n", ret);
        if (ret <= 0)
            break;
    }
    */


    buf_delete(buf);
    
    e_destroy(gmemhash);
    return 0;
}




#endif



